"""
行情数据服务
"""
import asyncio
import json
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from dataclasses import dataclass

import redis.asyncio as redis
from easyquotation import use

from ..core.config import settings
from ..core.database import db_manager

logger = logging.getLogger(__name__)


@dataclass
class MarketData:
    """行情数据"""
    symbol: str
    timestamp: datetime
    open: float
    high: float
    low: float
    close: float
    volume: int
    turnover: float
    bid_price: float
    ask_price: float
    bid_volume: int
    ask_volume: int
    delta: float = 0.0
    gamma: float = 0.0
    vega: float = 0.0
    theta: float = 0.0
    implied_volatility: float = 0.0


class MarketDataService:
    """行情数据服务"""
    
    def __init__(self):
        self.is_running = False
        self.redis_client: Optional[redis.Redis] = None
        self.quotation = None
        self.update_task: Optional[asyncio.Task] = None
        self.subscriptions: List[str] = []
        self.market_data_cache: Dict[str, MarketData] = {}
        self._lock = asyncio.Lock()
        
        # 期权合约映射
        self.option_symbols = {
            "510050.SH": "50ETF期权",
            "510300.SH": "300ETF期权", 
            "510500.SH": "500ETF期权"
        }
    
    async def start(self):
        """启动行情服务"""
        try:
            logger.info("正在启动行情服务...")
            
            # 连接Redis
            self.redis_client = redis.Redis(
                host=settings.REDIS_HOST,
                port=settings.REDIS_PORT,
                db=settings.REDIS_DB,
                password=settings.REDIS_PASSWORD,
                decode_responses=True
            )
            
            # 测试Redis连接
            await self.redis_client.ping()
            logger.info("Redis连接成功")
            
            # 初始化行情接口
            self.quotation = use('sina')  # 使用新浪行情
            
            # 设置默认订阅
            self.subscriptions = settings.SUBSCRIBED_SYMBOLS.copy()
            
            # 启动更新任务
            self.is_running = True
            self.update_task = asyncio.create_task(self._update_loop())
            
            logger.info("行情服务启动完成")
            
        except Exception as e:
            logger.error(f"行情服务启动失败: {e}")
            raise
    
    async def stop(self):
        """停止行情服务"""
        try:
            logger.info("正在停止行情服务...")
            
            self.is_running = False
            
            # 等待更新任务结束
            if self.update_task:
                self.update_task.cancel()
                try:
                    await self.update_task
                except asyncio.CancelledError:
                    pass
            
            # 关闭Redis连接
            if self.redis_client:
                await self.redis_client.close()
            
            logger.info("行情服务已停止")
            
        except Exception as e:
            logger.error(f"停止行情服务失败: {e}")
    
    async def _update_loop(self):
        """更新循环"""
        try:
            while self.is_running:
                try:
                    # 更新行情数据
                    await self._update_market_data()
                    
                    # 等待下次更新
                    await asyncio.sleep(settings.MARKET_DATA_INTERVAL)
                    
                except asyncio.CancelledError:
                    break
                except Exception as e:
                    logger.error(f"行情更新循环错误: {e}")
                    await asyncio.sleep(5)  # 出错后等待5秒
                    
        except Exception as e:
            logger.error(f"行情更新循环异常: {e}")
    
    async def _update_market_data(self):
        """更新行情数据"""
        try:
            if not self.subscriptions:
                return
            
            # 获取实时行情
            market_data = await self._fetch_realtime_data()
            
            # 更新缓存并保存到数据库
            for symbol, data in market_data.items():
                self.market_data_cache[symbol] = data
                
                # 保存到数据库
                await db_manager.save_market_data({
                    "symbol": symbol,
                    "timestamp": data.timestamp,
                    "open": data.open,
                    "high": data.high,
                    "low": data.low,
                    "close": data.close,
                    "volume": data.volume,
                    "turnover": data.turnover,
                    "bid_price": data.bid_price,
                    "ask_price": data.ask_price,
                    "bid_volume": data.bid_volume,
                    "ask_volume": data.ask_volume,
                    "delta": data.delta,
                    "gamma": data.gamma,
                    "vega": data.vega,
                    "theta": data.theta,
                    "implied_volatility": data.implied_volatility
                })
                
                # 发布到Redis
                await self._publish_market_data(symbol, data)
            
            logger.debug(f"行情数据更新完成，更新 {len(market_data)} 个品种")
            
        except Exception as e:
            logger.error(f"更新行情数据失败: {e}")
    
    async def _fetch_realtime_data(self) -> Dict[str, MarketData]:
        """获取实时行情数据"""
        try:
            market_data = {}
            
            # 获取股票行情
            stock_data = self.quotation.stocks(self.subscriptions)
            
            for symbol in self.subscriptions:
                if symbol in stock_data:
                    data = stock_data[symbol]
                    
                    # 创建行情数据对象
                    market_data[symbol] = MarketData(
                        symbol=symbol,
                        timestamp=datetime.now(),
                        open=float(data.get("open", 0)),
                        high=float(data.get("high", 0)),
                        low=float(data.get("low", 0)),
                        close=float(data.get("now", 0)),
                        volume=int(data.get("volume", 0)),
                        turnover=float(data.get("turnover", 0)),
                        bid_price=float(data.get("bid1", 0)),
                        ask_price=float(data.get("ask1", 0)),
                        bid_volume=int(data.get("bid1_volume", 0)),
                        ask_volume=int(data.get("ask1_volume", 0)),
                        delta=0.0,  # 期权希腊值需要专门计算
                        gamma=0.0,
                        vega=0.0,
                        theta=0.0,
                        implied_volatility=0.0
                    )
            
            # 模拟期权希腊值（实际应用中需要专门的期权定价模型）
            await self._calculate_greeks(market_data)
            
            return market_data
            
        except Exception as e:
            logger.error(f"获取实时行情失败: {e}")
            return {}
    
    async def _calculate_greeks(self, market_data: Dict[str, MarketData]):
        """计算期权希腊值（模拟实现）"""
        try:
            for symbol, data in market_data.items():
                # 这里应该使用实际的期权定价模型
                # 现在使用简单的模拟数据
                price = data.close
                
                # 模拟希腊值计算
                if price > 0:
                    data.delta = 0.5  # 平值期权delta约为0.5
                    data.gamma = 0.02  # 模拟gamma值
                    data.vega = 0.15  # 模拟vega值
                    data.theta = -0.01  # 模拟theta值
                    data.implied_volatility = 0.25  # 模拟隐含波动率
        
        except Exception as e:
            logger.error(f"计算希腊值失败: {e}")
    
    async def _publish_market_data(self, symbol: str, data: MarketData):
        """发布行情数据到Redis"""
        try:
            if not self.redis_client:
                return
            
            # 准备数据
            message = {
                "symbol": symbol,
                "timestamp": data.timestamp.isoformat(),
                "price": data.close,
                "volume": data.volume,
                "bid_price": data.bid_price,
                "ask_price": data.ask_price,
                "delta": data.delta,
                "gamma": data.gamma,
                "vega": data.vega,
                "theta": data.theta,
                "implied_volatility": data.implied_volatility
            }
            
            # 发布到Redis频道
            channel = f"market_data:{symbol}"
            await self.redis_client.publish(channel, json.dumps(message))
            
            # 同时发布到通用频道
            await self.redis_client.publish("market_data:all", json.dumps(message))
            
        except Exception as e:
            logger.error(f"发布行情数据失败: {e}")
    
    async def subscribe(self, symbols: List[str]) -> bool:
        """订阅行情"""
        try:
            async with self._lock:
                # 添加到订阅列表
                for symbol in symbols:
                    if symbol not in self.subscriptions:
                        self.subscriptions.append(symbol)
                        logger.info(f"已订阅 {symbol} 行情")
            
            return True
            
        except Exception as e:
            logger.error(f"订阅行情失败: {e}")
            return False
    
    async def unsubscribe(self, symbols: List[str]) -> bool:
        """取消订阅"""
        try:
            async with self._lock:
                # 从订阅列表移除
                for symbol in symbols:
                    if symbol in self.subscriptions:
                        self.subscriptions.remove(symbol)
                        logger.info(f"已取消订阅 {symbol} 行情")
            
            return True
            
        except Exception as e:
            logger.error(f"取消订阅失败: {e}")
            return False
    
    def get_market_data(self, symbol: str) -> Optional[MarketData]:
        """获取最新行情数据"""
        try:
            return self.market_data_cache.get(symbol)
            
        except Exception as e:
            logger.error(f"获取行情数据失败: {e}")
            return None
    
    def get_all_market_data(self) -> Dict[str, MarketData]:
        """获取所有行情数据"""
        try:
            return self.market_data_cache.copy()
            
        except Exception as e:
            logger.error(f"获取所有行情数据失败: {e}")
            return {}
    
    async def get_historical_data(self, symbol: str, days: int = 30) -> List[Dict]:
        """获取历史数据"""
        try:
            # 从数据库获取历史数据
            data = await db_manager.get_market_data(symbol, limit=days * 24 * 4)  # 假设每15分钟一条数据
            
            # 转换为标准格式
            historical_data = []
            for item in data:
                historical_data.append({
                    "timestamp": item["timestamp"],
                    "open": item["open"],
                    "high": item["high"],
                    "low": item["low"],
                    "close": item["close"],
                    "volume": item["volume"],
                    "delta": item["delta"],
                    "gamma": item["gamma"],
                    "vega": item["vega"],
                    "theta": item["theta"],
                    "implied_volatility": item["implied_volatility"]
                })
            
            return historical_data
            
        except Exception as e:
            logger.error(f"获取历史数据失败: {e}")
            return []
    
    async def get_option_chain(self, underlying: str) -> List[Dict]:
        """获取期权链"""
        try:
            # 这里应该获取实际的期权链数据
            # 现在返回模拟数据
            option_chain = []
            
            # 模拟不同行权价的期权
            current_price = 3.0  # 假设当前价格
            for strike in [2.8, 2.9, 3.0, 3.1, 3.2]:
                option_chain.append({
                    "symbol": f"{underlying}{strike:.1f}",
                    "underlying": underlying,
                    "strike": strike,
                    "option_type": "call" if strike >= current_price else "put",
                    "expiry": (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d"),
                    "price": abs(current_price - strike) * 0.1,  # 模拟期权价格
                    "delta": 0.5 if abs(current_price - strike) < 0.2 else 0.2,
                    "gamma": 0.02,
                    "vega": 0.15,
                    "theta": -0.01,
                    "implied_volatility": 0.25
                })
            
            return option_chain
            
        except Exception as e:
            logger.error(f"获取期权链失败: {e}")
            return []


# 全局行情服务实例
market_data_service = MarketDataService()
